Parte II: efecto de agregación

En la primera parte de la práctica vimos cómo la escala de análisis tiene influencia sobre los resultados del mismo. En esta segunda parte vamos a ver como diferentes formas de agregar los datos, en una misma escala, también pueden tener una influencia sobre los resultados de un análisis.

Para esta segunda parte vamos a volver a trabajar con los datos de mezcla de usos de suelo en la Ciudad de México y vamos a tratar de estimar su efecto en la generación de viajes. Los datos de uso de suelo están calculados a partir del DENUE, mientras que la información sobre los viajes es de la Encuesta Origen Destino del 2007, por lo que la escala de análisis serán los distritos de tráfico de dicha encuesta.

Como siempre, el primer paso es leer los datos:


In [3]:
from geopandas import GeoDataFrame
datos = GeoDataFrame.from_file('data/distritos_variables.shp')
datos.head()


Out[3]:
comercio cve_dist entrada geometry gid id loop ocio pob salida servicios viv
0 9614 034 201286 POLYGON ((486947.2187500237 2148842.250066909,... 1 1 27627 95 2125687 200217 579 32618
1 2905 083 209264 POLYGON ((478110.2187500393 2134697.250066519,... 2 2 76022 82 3563363 209859 949 42238
2 2944 113 65701 POLYGON ((501006.0312499983 2148019.750066887,... 3 3 10611 72 3128496 66706 459 26003
3 2425 131 42385 (POLYGON ((507974.718749986 2125105.250066254,... 4 4 8468 60 4842162 43106 256 44080
4 2849 063 128608 POLYGON ((487190.7500000236 2140396.250066676,... 5 5 24472 30 4146145 128756 441 35981

El shapefile que leimos tiene columnas para cada uno de los tipos de uso de suelo que nos interesan (vivienda, comercio, servicios y ocio), adicionalmente tiene tres columnas con información de los viajes:

  • entrada: los viajes que terminan en el distrito y que empezaron en uno diferente
  • salida: los viajes que empiezan en el distrito y terminan en uno distinto
  • loop: los viajes que inician y terminan en el mismo distrito.

Como pueden recordar de la práctica anterior en que usamos estos datos, la mezcla de usos de suelo se mide utilizando el índice de entropía, en esta ocasión vmos a calcularlo de acuerdo a la siguiente fórmula:

\begin{equation} E = \sum\limits_{j}{\frac{p_{j}*ln(p_{j})}{ln(J)}} \end{equation}

Donde $p_{j}$ representa la proporción del $j-ésimo$ uso de suelo con respecto al total y $J$ es el número de usos de suelo.


In [4]:
import numpy as np
intensidad = datos['comercio'] + datos['viv'] + datos['ocio'] + datos['servicios']
prop_comercio = datos['comercio'] / intensidad
prop_viv = datos['viv'] / intensidad
prop_ocio = datos['ocio'] / intensidad
prop_servicios = datos['servicios'] / intensidad
entropia = (prop_comercio*np.log(prop_comercio) + prop_viv*np.log(prop_viv) + prop_ocio*np.log(prop_ocio)
           + prop_servicios*np.log(prop_servicios))/np.log(4)
entropia.head()


Out[4]:
0   -0.443781
1   -0.250027
2   -0.303138
3   -0.178275
4   -0.235811
dtype: float64

Lo que hicimos para calcular la entropía es relativamente fácil. Primero creamos la variable intensidad, que es la suma de todos los usos de suelo y luego fuimos calculando cada una de las proporciones, finalmente las sumamos y las dividimos por el logaritmo natural del número de usus de suelo. Lo que tenemos ahora es una Serie que contiene los datos de entropía, ahora lo que necesitamos es unir esa serie a nuestros datos originales:


In [5]:
datos['entropia'] = entropia
datos.head()


Out[5]:
comercio cve_dist entrada geometry gid id loop ocio pob salida servicios viv entropia
0 9614 034 201286 POLYGON ((486947.2187500237 2148842.250066909,... 1 1 27627 95 2125687 200217 579 32618 -0.443781
1 2905 083 209264 POLYGON ((478110.2187500393 2134697.250066519,... 2 2 76022 82 3563363 209859 949 42238 -0.250027
2 2944 113 65701 POLYGON ((501006.0312499983 2148019.750066887,... 3 3 10611 72 3128496 66706 459 26003 -0.303138
3 2425 131 42385 (POLYGON ((507974.718749986 2125105.250066254,... 4 4 8468 60 4842162 43106 256 44080 -0.178275
4 2849 063 128608 POLYGON ((487190.7500000236 2140396.250066676,... 5 5 24472 30 4146145 128756 441 35981 -0.235811

Ahora que ya tenemos todos los datos, probemos un modelo que nos trate de explicar, por ejemplo, la cantidad de viajes que terminan en cada distrito. Lo primero que vamos a hacer, es explorar la colinearidad de las variables que tenemos:


In [6]:
corr = datos[['ocio','comercio','servicios','viv','entropia']].corr()
w, v = np.linalg.eig(corr)
print w


[ 2.75742273  1.38110304  0.2203412   0.02278048  0.61835256]

Parece ser que, si utilizamos las variables de arriba, vamos a tener problemas de colinearidad, entonces, para seleccionar las variables, observemos la matriz de correlación:


In [7]:
print corr


               ocio  comercio  servicios       viv  entropia
ocio       1.000000  0.714724   0.615029  0.586546 -0.348835
comercio   0.714724  1.000000   0.398002  0.455756 -0.592718
servicios  0.615029  0.398002   1.000000  0.389734 -0.353215
viv        0.586546  0.455756   0.389734  1.000000  0.349609
entropia  -0.348835 -0.592718  -0.353215  0.349609  1.000000

Como puden ver, la entropía está bastante correlacionada con las demás variables, sobre todo con comercio y ocio, seleccionesmos entonces, por lo pronto, entropía y vivienda como variables explicativas.

Ahora, antes de correr nuestro modelo, vamos a normalizar las variables porque tiene escalas de variación muy diferentes y ya sabemos que eso nos puede traer problemas:


In [8]:
variables = datos[['entrada','viv','entropia']]
vars_norm = (variables - variables.mean())/variables.std()
print vars_norm.mean()
print vars_norm.std()


entrada    -1.209858e-17
viv         2.085227e-16
entropia    2.306399e-15
dtype: float64
entrada     1
viv         1
entropia    1
dtype: float64

Ahora que ya seleccionamos las varibles y las normalizamos, vamos a correr un primer modelo:


In [9]:
import statsmodels.formula.api as sm
model = sm.ols(formula="entrada ~ viv + entropia",data=vars_norm).fit()
print model.summary()


                            OLS Regression Results                            
==============================================================================
Dep. Variable:                entrada   R-squared:                       0.211
Model:                            OLS   Adj. R-squared:                  0.201
Method:                 Least Squares   F-statistic:                     20.35
Date:                Wed, 06 Apr 2016   Prob (F-statistic):           1.47e-08
Time:                        09:54:03   Log-Likelihood:                -200.88
No. Observations:                 155   AIC:                             407.8
Df Residuals:                     152   BIC:                             416.9
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [95.0% Conf. Int.]
------------------------------------------------------------------------------
Intercept      0.0041      0.072      0.057      0.955        -0.138     0.146
viv            0.2265      0.078      2.908      0.004         0.073     0.380
entropia      -0.4868      0.077     -6.337      0.000        -0.639    -0.335
==============================================================================
Omnibus:                       39.203   Durbin-Watson:                   2.076
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               81.687
Skew:                           1.116   Prob(JB):                     1.83e-18
Kurtosis:                       5.768   Cond. No.                         1.44
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

Ejercicio:

Especfique diferentes modelos para tratar de explicar las tres variables de viajes.

Cambiando de escala

Ahora vamos a cambiar la escala del análisis agregando los distritos en unidades más grandes, para hacer esto, primero vamos a crear regionalizaciones al azar a partir de los datos:


In [27]:
import clusterpy
datos_cp = clusterpy.importArcData('data/distritos_variables')
datos_cp.cluster('random',[datos_cp.fieldNames[0]],50,dissolve=0)


Loading data/distritos_variables.dbf
Loading data/distritos_variables.shp
Done
Getting variables
Variables successfully extracted
FINAL SOLUTION:  [42, 0, 19, 36, 35, 4, 16, 21, 3, 9, 0, 25, 18, 7, 29, 14, 19, 21, 42, 43, 19, 34, 46, 28, 33, 42, 37, 7, 10, 37, 49, 43, 4, 22, 16, 37, 49, 45, 37, 14, 6, 24, 24, 2, 3, 33, 5, 28, 26, 35, 33, 5, 4, 28, 18, 20, 15, 35, 33, 19, 26, 30, 22, 28, 28, 5, 45, 19, 22, 38, 36, 49, 16, 36, 47, 37, 9, 37, 34, 25, 0, 7, 36, 21, 8, 30, 0, 1, 34, 19, 37, 33, 28, 44, 12, 21, 48, 42, 15, 17, 34, 20, 42, 7, 37, 33, 22, 28, 13, 41, 15, 42, 27, 42, 32, 22, 0, 23, 5, 28, 13, 8, 35, 40, 42, 2, 42, 6, 36, 49, 19, 31, 7, 31, 10, 2, 9, 47, 34, 28, 30, 7, 17, 39, 43, 37, 39, 17, 13, 11, 35, 43, 18, 5, 28, 22]
FINAL OF:  217782.0
Done
Adding variables
Done

Recuerden que para ver las regiones resultantes pueden exportar los resultados como shapefile usando datos_cp.exportArcData('ruta/para/guardar'). Por lo pronto omitiremos este paso y más bien vamos a unir el resultado de los clusters a los datos originales para poder hacer algunas operaciones.

El primer paso es saber en qué columna guardo el algoritmo los resultados de la regionalización, para esto, vamos a ver qué columnas tiene nuestro objeto:


In [28]:
datos_cp.fieldNames


Out[28]:
['ID',
 'comercio',
 'cve_dist',
 'entrada',
 'gid',
 'id',
 'loop',
 'ocio',
 'pob',
 'salida',
 'servicios',
 'viv',
 'random_20160406101421']

Como pueden ver, el algoritmo le pone un nombre al azar a la columna donde va a guardar los resultados, random_20160405130236, en este caso.

Ahora lo que vamos a hacer es extraer sólo el identificador de distrito y el identificador de región, que es lo que necesitamos para hacer un merge con nuestros datros originales:


In [29]:
resultado = datos_cp.getVars(['cve_dist','random_20160406101421'])
print resultado


Getting variables
Variables successfully extracted
{0: ['034', 42], 1: ['083', 0], 2: ['113', 19], 3: ['131', 36], 4: ['063', 35], 5: ['086', 4], 6: ['135', 16], 7: ['056', 21], 8: ['024', 3], 9: ['060', 9], 10: ['071', 0], 11: ['156', 25], 12: ['149', 18], 13: ['107', 7], 14: ['001', 29], 15: ['098', 14], 16: ['114', 19], 17: ['074', 21], 18: ['032', 42], 19: ['103', 43], 20: ['117', 19], 21: ['061', 34], 22: ['145', 46], 23: ['048', 28], 24: ['104', 33], 25: ['029', 42], 26: ['055', 37], 27: ['150', 7], 28: ['091', 10], 29: ['053', 37], 30: ['079', 49], 31: ['099', 43], 32: ['009', 4], 33: ['140', 22], 34: ['146', 16], 35: ['037', 37], 36: ['084', 49], 37: ['039', 45], 38: ['036', 37], 39: ['022', 14], 40: ['080', 6], 41: ['016', 24], 42: ['094', 24], 43: ['110', 2], 44: ['027', 3], 45: ['151', 33], 46: ['018', 5], 47: ['049', 28], 48: ['010', 26], 49: ['043', 35], 50: ['153', 33], 51: ['096', 5], 52: ['008', 4], 53: ['072', 28], 54: ['155', 18], 55: ['015', 20], 56: ['126', 15], 57: ['052', 35], 58: ['144', 33], 59: ['118', 19], 60: ['092', 26], 61: ['042', 30], 62: ['139', 22], 63: ['044', 28], 64: ['085', 28], 65: ['013', 5], 66: ['006', 45], 67: ['119', 19], 68: ['020', 22], 69: ['041', 38], 70: ['127', 36], 71: ['078', 49], 72: ['147', 16], 73: ['132', 36], 74: ['076', 47], 75: ['033', 37], 76: ['058', 9], 77: ['062', 37], 78: ['066', 34], 79: ['152', 25], 80: ['046', 0], 81: ['108', 7], 82: ['129', 36], 83: ['116', 21], 84: ['128', 8], 85: ['047', 30], 86: ['082', 0], 87: ['101', 1], 88: ['069', 34], 89: ['120', 19], 90: ['054', 37], 91: ['154', 33], 92: ['073', 28], 93: ['068', 44], 94: ['011', 12], 95: ['057', 21], 96: ['143', 48], 97: ['038', 42], 98: ['124', 15], 99: ['003', 17], 100: ['064', 34], 101: ['014', 20], 102: ['028', 42], 103: ['105', 7], 104: ['112', 37], 105: ['109', 33], 106: ['142', 22], 107: ['088', 28], 108: ['136', 13], 109: ['134', 41], 110: ['121', 15], 111: ['005', 42], 112: ['067', 27], 113: ['031', 42], 114: ['051', 32], 115: ['021', 22], 116: ['081', 0], 117: ['025', 23], 118: ['012', 5], 119: ['089', 28], 120: ['097', 13], 121: ['125', 8], 122: ['007', 35], 123: ['017', 40], 124: ['026', 42], 125: ['111', 2], 126: ['030', 42], 127: ['065', 6], 128: ['133', 36], 129: ['130', 49], 130: ['115', 19], 131: ['093', 31], 132: ['141', 7], 133: ['095', 31], 134: ['090', 10], 135: ['023', 2], 136: ['059', 9], 137: ['075', 47], 138: ['077', 34], 139: ['087', 28], 140: ['045', 30], 141: ['102', 7], 142: ['004', 17], 143: ['122', 39], 144: ['100', 43], 145: ['035', 37], 146: ['123', 39], 147: ['002', 17], 148: ['137', 13], 149: ['070', 11], 150: ['040', 35], 151: ['106', 43], 152: ['138', 18], 153: ['019', 5], 154: ['050', 28], 155: ['148', 22]}

El resultado es un diccionario, es decir, un conjunto de parejas llave : valor, en este caso las llaves son el id de los polígonos y los valores son la clave del distrito y el identificador de la región. Como queremos unir estos resultados con nuestros datos originales, necesitamos convertirlos en un DataFrame, afortunadamete esto es relativamente fácil:


In [30]:
import pandas as pd
df = pd.DataFrame(resultado)
df.head()


Out[30]:
0 1 2 3 4 5 6 7 8 9 ... 146 147 148 149 150 151 152 153 154 155
0 034 083 113 131 063 086 135 056 024 060 ... 123 002 137 070 040 106 138 019 050 148
1 42 0 19 36 35 4 16 21 3 9 ... 39 17 13 11 35 43 18 5 28 22

2 rows × 156 columns

Casi lo logramos, el único problema es que los datos están "al revés", es decir renglones y columnas están intercambiados. Lo que necesitamos es trasponerlos:º


In [31]:
df = df.transpose()
df.head()


Out[31]:
0 1
0 034 42
1 083 0
2 113 19
3 131 36
4 063 35

Ahora pongámosle nombre a las columnas, la primera es el identificador de distrito y la segunda el de región:


In [32]:
df.columns=['cve_dist','id_region']
df.head()


Out[32]:
cve_dist id_region
0 034 42
1 083 0
2 113 19
3 131 36
4 063 35

Ahora sí podemos hacer un merge con los datos originales:


In [33]:
region_1 = datos.merge(df,how='inner',on='cve_dist')
region_1.head()


Out[33]:
comercio cve_dist entrada geometry gid id loop ocio pob salida servicios viv entropia id_region
0 9614 034 201286 POLYGON ((486947.2187500237 2148842.250066909,... 1 1 27627 95 2125687 200217 579 32618 -0.443781 42
1 2905 083 209264 POLYGON ((478110.2187500393 2134697.250066519,... 2 2 76022 82 3563363 209859 949 42238 -0.250027 0
2 2944 113 65701 POLYGON ((501006.0312499983 2148019.750066887,... 3 3 10611 72 3128496 66706 459 26003 -0.303138 19
3 2425 131 42385 (POLYGON ((507974.718749986 2125105.250066254,... 4 4 8468 60 4842162 43106 256 44080 -0.178275 36
4 2849 063 128608 POLYGON ((487190.7500000236 2140396.250066676,... 5 5 24472 30 4146145 128756 441 35981 -0.235811 35

Ahora ya tenemos casi todo lo que necesitamos para correr un nuevo modelo, esta vez sobre las variables agregadas en regiones, lo único que necesitamos es, justamente, calcular las variables agregadas, para esto, vamos a hacer un "gropup by":


In [34]:
agregados = region_1.groupby(by='id_region').sum()
agregados.head()


Out[34]:
comercio entrada gid id loop ocio pob salida servicios viv entropia
id_region
0 12371 607672 294 294 227979 312 20183388 607606 2593 198099 -1.111152
1 3400 80246 88 88 15495 73 5120453 82041 550 40501 -0.250569
2 8809 256866 323 323 50293 199 11091598 256860 1451 97818 -0.794170
3 4735 227763 74 74 30650 89 5164621 227211 909 60093 -0.489324
4 6053 437583 114 114 64200 170 5426096 436799 1692 73677 -0.739684

Aquí simplemente agrupamos los datos por su id de región y calculamos la suma de las variables sobre cada grupo.

Sólo hay un problema, la entropía quedo calculada como la suma de las entropías individuales y eso no nos sirve, necesitamos volver a calcularla:


In [35]:
intensidad = agregados['comercio'] + agregados['viv'] + agregados['ocio']
prop_comercio = agregados['comercio'] / intensidad
prop_viv = agregados['viv'] / intensidad
prop_ocio = agregados['ocio'] / intensidad
entropia = (prop_comercio*np.log(prop_comercio) + prop_viv*np.log(prop_viv) + prop_ocio*np.log(prop_ocio))/np.log(3)
agregados.ix[:,'entropia']= entropia
agregados.head()


Out[35]:
comercio entrada gid id loop ocio pob salida servicios viv entropia
id_region
0 12371 607672 294 294 227979 312 20183388 607606 2593 198099 -0.213346
1 3400 80246 88 88 15495 73 5120453 82041 550 40501 -0.258801
2 8809 256866 323 323 50293 199 11091598 256860 1451 97818 -0.271387
3 4735 227763 74 74 30650 89 5164621 227211 909 60093 -0.247112
4 6053 437583 114 114 64200 170 5426096 436799 1692 73677 -0.257899

Ahora sí, podemos correr el mismo modelo de antes pero sobre nuestros datos agregados (recordemos que antes hay que normalizarlos):


In [43]:
variables = agregados[['entrada','viv','entropia']]
vars_norm = (variables - variables.mean())/variables.std()
model = sm.ols(formula="entrada ~ viv + entropia",data=vars_norm).fit()
print model.summary()


                            OLS Regression Results                            
==============================================================================
Dep. Variable:                entrada   R-squared:                       0.635
Model:                            OLS   Adj. R-squared:                  0.620
Method:                 Least Squares   F-statistic:                     40.93
Date:                Wed, 06 Apr 2016   Prob (F-statistic):           5.08e-11
Time:                        10:38:26   Log-Likelihood:                -45.226
No. Observations:                  50   AIC:                             96.45
Df Residuals:                      47   BIC:                             102.2
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [95.0% Conf. Int.]
------------------------------------------------------------------------------
Intercept  -1.388e-17      0.087  -1.59e-16      1.000        -0.175     0.175
viv            0.7664      0.088      8.686      0.000         0.589     0.944
entropia      -0.2670      0.088     -3.026      0.004        -0.445    -0.090
==============================================================================
Omnibus:                        3.867   Durbin-Watson:                   1.945
Prob(Omnibus):                  0.145   Jarque-Bera (JB):                3.670
Skew:                           0.649   Prob(JB):                        0.160
Kurtosis:                       2.726   Cond. No.                         1.06
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

Ahora, el objetivo es comparar los resultados usando diferentes regionalizaciones aleatorias de los mismos datos. El proceso de pasar los datos a regiones es un poco largo, pero no se preocupen, para aliviar ese trabajo escribi una función que se encarga de encapsular todo el proceso:


In [40]:
def regionaliza_agrega(dataframe,shp_path='data/distritos_variables',n_regiones=50):
    datos = clusterpy.importArcData(shp_path)
    datos.cluster('random',[datos.fieldNames[0]],50,dissolve=0)
    resultado = datos.getVars(['cve_dist',datos.fieldNames[-1]])
    df = pd.DataFrame(resultado)
    df = df.transpose()
    df.columns=['cve_dist','id_region']
    region = dataframe.merge(df,how='inner',on='cve_dist')
    agregados = region.groupby(by='id_region').sum()
    intensidad = agregados['comercio'] + agregados['viv'] + agregados['ocio']
    prop_comercio = agregados['comercio'] / intensidad
    prop_viv = agregados['viv'] / intensidad
    prop_ocio = agregados['ocio'] / intensidad
    entropia = (prop_comercio*np.log(prop_comercio) + prop_viv*np.log(prop_viv) + prop_ocio*np.log(prop_ocio))/np.log(3)
    agregados.ix[:,'entropia']= entropia
    return agregados

La función nos regresa los datos agregados en regiones aleatorias, lo único que necesitamos es selecionar las varables que vaos a usar, normalizarlas y correr un modelo:


In [45]:
agregados = regionaliza_agrega(datos)
variables = agregados[['entrada','viv','entropia']]
vars_norm = (variables - variables.mean())/variables.std()
model = sm.ols(formula="entrada ~ viv + entropia",data=vars_norm).fit()
print model.summary()


Loading data/distritos_variables.dbf
Loading data/distritos_variables.shp
Done
Getting variables
Variables successfully extracted
FINAL SOLUTION:  [7, 26, 20, 0, 7, 1, 9, 18, 25, 45, 17, 32, 38, 40, 37, 11, 41, 16, 7, 42, 13, 6, 10, 24, 42, 25, 20, 38, 14, 7, 6, 34, 37, 38, 9, 7, 48, 47, 43, 11, 5, 3, 22, 31, 25, 35, 25, 19, 28, 47, 29, 11, 37, 19, 8, 14, 18, 7, 4, 41, 14, 2, 21, 2, 19, 12, 47, 20, 11, 37, 23, 48, 9, 15, 23, 7, 45, 6, 5, 38, 33, 29, 0, 13, 13, 24, 17, 4, 5, 7, 20, 29, 33, 37, 12, 27, 38, 7, 44, 37, 5, 12, 25, 29, 4, 38, 40, 19, 22, 22, 13, 37, 37, 25, 33, 11, 17, 4, 37, 39, 38, 13, 47, 36, 25, 4, 25, 5, 15, 23, 41, 22, 40, 22, 39, 30, 20, 45, 6, 49, 24, 46, 37, 18, 31, 20, 18, 37, 38, 17, 47, 29, 21, 25, 2, 21]
FINAL OF:  216934.0
Done
Adding variables
Done
Getting variables
Variables successfully extracted
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                entrada   R-squared:                       0.586
Model:                            OLS   Adj. R-squared:                  0.569
Method:                 Least Squares   F-statistic:                     33.32
Date:                Wed, 06 Apr 2016   Prob (F-statistic):           9.76e-10
Time:                        10:56:02   Log-Likelihood:                -48.370
No. Observations:                  50   AIC:                             102.7
Df Residuals:                      47   BIC:                             108.5
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [95.0% Conf. Int.]
------------------------------------------------------------------------------
Intercept   2.776e-17      0.093   2.99e-16      1.000        -0.187     0.187
viv            0.7210      0.094      7.686      0.000         0.532     0.910
entropia      -0.2569      0.094     -2.738      0.009        -0.446    -0.068
==============================================================================
Omnibus:                       40.717   Durbin-Watson:                   2.697
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              223.878
Skew:                           1.866   Prob(JB):                     2.43e-49
Kurtosis:                      12.671   Cond. No.                         1.01
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

Como pueden ver, cada vez que corran la celda anterior, van a tener un resultado un poco diferente, es decir, cada modelo arroja resultados diferentes en la misma escala. En este caso, podemos pensar que no es muy grave, nosotros sabemos que las agregaciones son aleatorias y tenemos alguna certeza de que las desviaciones observadas en los resultados son aleatorias, pero ¿Qué pasa cuando no tenemos control sobre las agregaciones?

Antes de examinar esa pregunta, hay un...

Ejercicio

Encuentren un modelo que explique razonablemente bien los viajes entrantes a la escala agregada (para esto usen una sóla regionalización aleatoria, es decir, no usen la función que les acabo de explicar). A aprtir de ese modelo, ahora sí, usando la función regionaliza_agrega, haz diferentes experimentos y registra los parámetros más importantes: la $R^2$ y los coeficientes de las variables. Con los resultados de varias repeticiones, trata de mostrar que las diferencias son aleatorias (es decir, que siguen una distribución normal).

Epílogo ¿Qué pasa cuando no tenemos control sobre las agregaciones?

Ahora vamos a trabajar sobre tres agregaciones diferentes de los datos, en la misma escala que las que hicimos, pero sobre las que no tenemos control. Simplemente son las unidades que tenemos (más o menos como las AGEBS, por ejemplo)

Lo primero que vamos a hacer es leer los nuevos datos:


In [47]:
datos_nuevos = GeoDataFrame.from_file('data/variables_regiones.shp')
datos_nuevos.head()


Out[47]:
ID comercio cve_dist entrada entropia geometry gid id_1 loop ocio pob region_1 region_2 region_3 salida servicios viv
0 1 9614 034 201286 -0.443781 POLYGON ((486947.2187500237 2148842.250066909,... 1 None 27627 95 2125687 25 16 20 200217 579 32618
1 2 2905 083 209264 -0.250027 POLYGON ((478110.2187500393 2134697.250066519,... 2 None 76022 82 3563363 25 20 4 209859 949 42238
2 3 2944 113 65701 -0.303138 POLYGON ((501006.0312499983 2148019.750066887,... 3 None 10611 72 3128496 29 43 17 66706 459 26003
3 4 2425 131 42385 -0.178275 (POLYGON ((507974.718749986 2125105.250066254,... 4 None 8468 60 4842162 21 42 23 43106 256 44080
4 5 2849 063 128608 -0.235811 POLYGON ((487190.7500000236 2140396.250066676,... 5 None 24472 30 4146145 38 20 2 128756 441 35981

Como pueden ver, son exáctamente igual a los originales, salvo porque tienen tres columnas extra: region_1, region_2 y region_3. Esta son, jústamente, las nuevas agregaciones de los datos, por lo demás, todo es exáctamente igiual que antes.

Entonces, construyamos tres agregaciones, una para cada regionalización, y ajustemos nuestro modelo lineal para ver qué sucede. Empecemos con la primera regionalización:


In [49]:
r_1 = datos_nuevos.groupby(by='region_1').sum()
intensidad = r_1['comercio'] + r_1['viv'] + r_1['ocio']
prop_comercio = r_1['comercio'] / intensidad
prop_viv = r_1['viv'] / intensidad
prop_ocio = r_1['ocio'] / intensidad
entropia = (prop_comercio*np.log(prop_comercio) + prop_viv*np.log(prop_viv) + prop_ocio*np.log(prop_ocio))/np.log(3)
r_1.ix[:,'entropia']= entropia
variables = r_1[['entrada','viv','entropia']]
vars_norm = (variables - variables.mean())/variables.std()
model_1 = sm.ols(formula="entrada ~ viv + entropia",data=vars_norm).fit()
print model_1.summary()


                            OLS Regression Results                            
==============================================================================
Dep. Variable:                entrada   R-squared:                       0.759
Model:                            OLS   Adj. R-squared:                  0.749
Method:                 Least Squares   F-statistic:                     72.60
Date:                Wed, 06 Apr 2016   Prob (F-statistic):           5.88e-15
Time:                        12:46:46   Log-Likelihood:                -34.438
No. Observations:                  49   AIC:                             74.88
Df Residuals:                      46   BIC:                             80.55
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [95.0% Conf. Int.]
------------------------------------------------------------------------------
Intercept     -0.0057      0.072     -0.079      0.937        -0.151     0.139
viv            0.8412      0.074     11.318      0.000         0.692     0.991
entropia      -0.1365      0.074     -1.839      0.072        -0.286     0.013
==============================================================================
Omnibus:                       23.529   Durbin-Watson:                   2.440
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               81.090
Skew:                           1.045   Prob(JB):                     2.46e-18
Kurtosis:                       8.946   Cond. No.                         1.22
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

Ahora con la segunda:


In [50]:
r_2 = datos_nuevos.groupby(by='region_2').sum()
intensidad = r_2['comercio'] + r_2['viv'] + r_2['ocio']
prop_comercio = r_2['comercio'] / intensidad
prop_viv = r_2['viv'] / intensidad
prop_ocio = r_2['ocio'] / intensidad
entropia = (prop_comercio*np.log(prop_comercio) + prop_viv*np.log(prop_viv) + prop_ocio*np.log(prop_ocio))/np.log(3)
r_2.ix[:,'entropia']= entropia
variables = r_2[['entrada','viv','entropia']]
vars_norm = (variables - variables.mean())/variables.std()
model_2 = sm.ols(formula="entrada ~ viv + entropia",data=vars_norm).fit()
print model_2.summary()


                            OLS Regression Results                            
==============================================================================
Dep. Variable:                entrada   R-squared:                       0.847
Model:                            OLS   Adj. R-squared:                  0.840
Method:                 Least Squares   F-statistic:                     129.6
Date:                Wed, 06 Apr 2016   Prob (F-statistic):           7.44e-20
Time:                        12:48:00   Log-Likelihood:                -23.586
No. Observations:                  50   AIC:                             53.17
Df Residuals:                      47   BIC:                             58.91
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [95.0% Conf. Int.]
------------------------------------------------------------------------------
Intercept   1.388e-17      0.057   2.45e-16      1.000        -0.114     0.114
viv            0.9349      0.058     16.101      0.000         0.818     1.052
entropia      -0.1647      0.058     -2.836      0.007        -0.281    -0.048
==============================================================================
Omnibus:                        4.976   Durbin-Watson:                   2.009
Prob(Omnibus):                  0.083   Jarque-Bera (JB):                4.062
Skew:                           0.479   Prob(JB):                        0.131
Kurtosis:                       4.017   Cond. No.                         1.20
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

Ahora ustedes pueden hacer solitos el tercer modelo.

¿Qué es lo que está pasando? ¿Por qué estamos obteniendo ahora valores tan diferentes a lo que teníamos antes?

La parte final de su tarea es tratar de explicar esto.


In [ ]: